/*
** cliente utilizando select() e controle de buffer
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/times.h>
#include <sys/timeb.h>
#include <unistd.h>

#define PORT 3477  
#define MAXDATASIZE 100
#define TIMEOUT 2

// FUNCOES AUXILIARES
double difftimeb (struct timeb* end, struct timeb* begin);
void   set_time  (struct timeval *time);

int main(int argc, char *argv[]) {

  /* ---------------------VARIAVEIS------------------------ */
                                                            //
  int sockfd;                                               //
  fd_set r_fds, w_fds;                                      //
  FILE *wsock, *rsock;                                      //
  char buf[MAXDATASIZE];                                    //
                                                            //
  int tam_linha;                                            //
  int linhas_enviadas=0, linhas_recebidas=0;                //
  int char_enviados=0, char_recebidos=0, maior_linha=0;     //
                                                            //
  struct timeval time;                                      //
  struct timeb t_init, t_end;                               //
  double t_total;                                           //
                                                            //
  struct hostent *he;                                       //
  struct sockaddr_in their_addr;                            //
                                                            //
  // ------------------------------------------------------ //
  //
  /* ------------------------INICIALIZACAO------------------------------ */
                                                                         //
  FD_ZERO(&r_fds);                                                       //
  FD_ZERO(&w_fds);                                                       //
                                                                         //
  if (argc != 2) {                                                       //
    fprintf(stderr,"usage: client hostname\n");                          //
    exit(1);                                                             //
  }                                                                      //
                                                                         //
  if ((he=gethostbyname(argv[1])) == NULL) {                             // 
    perror("gethostbyname");                                             //
    exit(1);                                                             //
  }                                                                      //
                                                                         //
  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {                //
    perror("socket");                                                    //
    exit(1);                                                             //
  }                                                                      //
                                                                         //
  wsock = fdopen (sockfd, "w");                                          //
  rsock = fdopen (sockfd, "r");                                          //
                                                                         //
  setvbuf(wsock, NULL, _IOLBF, 0);                                       //
  setvbuf(rsock, NULL, _IOLBF, 0);                                       //
                                                                         //
  their_addr.sin_family = AF_INET;                                       //                      
  their_addr.sin_port   = htons(PORT);                                   //                      
  their_addr.sin_addr   = *((struct in_addr *)he->h_addr);               //     
  bzero(&(their_addr.sin_zero), 8);                                      //     
                                                                         //     
  if (connect(sockfd, (struct sockaddr *)&their_addr,                    //    
	      sizeof(struct sockaddr)) == -1) {                          //    
    perror("connect");                                                   //    
    exit(1);                                                             //    
  }                                                                      //    
                                                                         //    
  ftime(&t_init);                                                        //    
                                                                         //
  // ------------------------------------------------------------------- //
  //
  /* ------------------------INTERACOES--------------------------- */
                                                                   //
  while(1) {                                                       //
                                                                   //
    set_time(&time);                                               //
                                                                   //
    FD_SET(sockfd, &r_fds);                                        //
    FD_SET(sockfd, &w_fds);                                        //
                                                                   //
    // -----------------ENVIO------------------ //                 //
                                                                   //
    if(fgets (buf, MAXDATASIZE, stdin)) {                          //
      select(sockfd+1, NULL, &w_fds, NULL, &time);                 //
                                                                   //
      if(FD_ISSET(sockfd, &w_fds))                                 //
	fputs(buf, wsock);                                         //
                                                                   //
      linhas_enviadas++;                                           //
                                                                   //
      tam_linha      = strlen(buf);                                //
      char_enviados += tam_linha;                                  //
                                                                   //
      if(tam_linha > maior_linha)                                  //
	maior_linha = tam_linha;                                   //
                                                                   //
      // -------------RECEBIMENTO--------------- //                //
                                                                   //
      select (sockfd+1, &r_fds, NULL, NULL, &time);                //
	                                                           //
      if(FD_ISSET(sockfd, &r_fds)) {                               //
	fgets  (buf, MAXDATASIZE, rsock);                          //
	fflush (rsock);                                            //
                                                                   //
	linhas_recebidas++;                                        //
                                                                   //
	tam_linha       = strlen(buf);                             //
	char_recebidos += tam_linha;                               //
                                                                   //
	fputs(buf, stdout);                                        //
      }                                                            //
    }                                                              //
                                                                   //
    else                                                           //
      break;                                                       //
  }                                                                //
                                                                   //
  // ------------------------------------------------------------- //
  //
  /* ---------------CONTAGEM DO TEMPO E IMPRESSOES------------------- */
                                                                      //
  ftime(&t_end);                                                      //
  t_total = difftimeb(&t_end, &t_init);                               //
                                                                      //
  fprintf(stderr, "TEMPO TOTAL: %.3lf\n", t_total);                   //
  fprintf(stderr, "Linhas enviadas:  %d\n", linhas_enviadas);         //
  fprintf(stderr, "Linhas recebidas: %d\n", linhas_recebidas);        //
  fprintf(stderr, "Caract. enviados:  %d\n", char_enviados);          //
  fprintf(stderr, "Caract. recebidos: %d\n", char_recebidos);         //
  fprintf(stderr, "Maior linha: %d\n\n", maior_linha);                //
                                                                      //
  // ---------------------------------------------------------------- //

  close (sockfd);
  return 0;   
}

void set_time(struct timeval *time) {
  time->tv_sec  = TIMEOUT;
  time->tv_usec = 0;
  
  return;
}

double difftimeb(struct timeb* end, struct timeb* begin) {
  double sec, milli;
  
  sec = (double)end->time - (double)begin->time;
  milli = (double)end->millitm - (double)begin->millitm;
  
  if (milli < 0) {
    sec--;
    milli += 1000.0;
  }
  
  sec += milli / 1000.0;
  
  return sec;
}
